package cn.com.main;

import cn.com.obs.S3Utils;
import cn.com.obs.entity.ClusterSubAccountRespDto;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.obs.services.model.HttpMethodEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class BillExport {


    public static final String ACCESS_KEY = "et4AyzzgmstKkvsHSHlI";

    public static final String SECRET_KEY = "XglPajavcwudGWgm5bnQCfgT5GElgI2ZoeVM9oZW";

    public static final String OBS_URL = "https://10.106.3.111:32296";

    public static final String BUCKET_NAME = "bssadmin-sub";

    public static ClusterSubAccountRespDto mockAccount = new ClusterSubAccountRespDto();

    static {
        mockAccount.setAccessKey(ACCESS_KEY);
        mockAccount.setSecretKey(SECRET_KEY);
        mockAccount.setObsUrl(OBS_URL);
        mockAccount.setBucketName(BUCKET_NAME);
    }

    public static void main(String[] args) {


        // 账单数据示例
        List<List<String>> billDataList = List.of(
                List.of("Date,Amount,Description", "2024-05-25,100,Example1"),
                List.of("Date,Amount,Description", "2024-05-26,200,Example2")
        );
        recordFiling(billDataList);
    }

    public static void recordFiling(List<List<String>> billDataList) {
        try {

            // 获取当前时间并格式化
            String timestamp = DateUtil.format(roundTime(new DateTime()), "yyyyMMddHHmm");
            String baseDir = "cdr-" + timestamp.substring(0, 8) + timestamp.substring(8, 10) + "00";
            String tarGzFileName = baseDir + ".tar.gz";
            String bakfilesDir = baseDir + "/bakfiles";
            String bmsDir = bakfilesDir + "/bms";
            String zipFileName = "HWS_cn-central_modelmate.notebook_" + timestamp + ".zip";
            String sdrPath = "sdr-bakfiles";

            // 创建目录
            Files.createDirectories(Paths.get(bmsDir));
            Files.createDirectories(Paths.get(sdrPath));
            for (int i = 0; i < 3; i++) {
                // 创建 CSV 文件
                for (int j = 0; j < billDataList.size(); j++) {
                    String fileName = "HWS_cn-central_modelmate.notebook_" + timestamp + "_" + (j + 1) + ".csv";
                    createCsvFile(sdrPath, fileName, billDataList.get(j));
                }
                // 压缩 CSV 文件为 ZIP 文件并嵌套
                Path nestedZipFilePath = compressNestedZipFiles(bmsDir, i + zipFileName, sdrPath);
            }


            // 将整个 bakfiles 目录压缩为 tar.gz 并输出为流
            compressDirectoryToTarGz(tarGzFileName, baseDir);

            // 将流输出到指定目录
            String outputFilePath = timestamp + "/" + baseDir + ".tar.gz";

            //上传文件
            uploadFiles(outputFilePath, baseDir);
            // 清理目录
            deleteDirectory(Paths.get(baseDir).toFile());
        } catch (IOException e) {
            log.error("账单导出失败", e);
        }
    }

    public static DateTime roundTime(DateTime time) {
        int minute = time.minute();
        if (minute >= 30) {
            time = DateUtil.offsetHour(time, 1);
        }
        return DateUtil.beginOfHour(time);
    }

    /**
     * 创建csv文件
     *
     * @param dir      dir
     * @param fileName 文件名
     * @param data     数据
     * @throws IOException IOException
     */
    private static void createCsvFile(String dir, String fileName, List<String> data) throws IOException {
        Path filePath = Paths.get(dir, fileName);
        try (BufferedWriter writer = Files.newBufferedWriter(filePath)) {
            for (String line : data) {
                writer.write(line);
                writer.newLine();
            }
        }
    }

    /**
     * 压缩嵌套zip文件
     *
     * @param dir         dir
     * @param zipFileName zip文件名
     * @return {@link Path }
     * @throws IOException IOException
     */
    private static Path compressNestedZipFiles(String dir, String zipFileName, String sdrPath) throws IOException {
        Path firstZip = compressAllFilesInFolder(dir.substring(0, dir.lastIndexOf("/")), zipFileName, sdrPath);
        Path path = compressFilesIntoZip(dir, zipFileName, zipFileName, firstZip);
        Files.delete(firstZip);
        return path;
    }

    /**
     * 将目录压缩到targz
     *
     * @param tarGzFileName tar gz文件名
     * @param sourceDir     源目录
     * @throws IOException IOException
     */
    private static void compressDirectoryToTarGz(String tarGzFileName, String sourceDir) throws IOException {
        FileOutputStream fos = new FileOutputStream(tarGzFileName);
        try (BufferedOutputStream bos = new BufferedOutputStream(fos);
             GzipCompressorOutputStream gcos = new GzipCompressorOutputStream(bos);
             TarArchiveOutputStream taos = new TarArchiveOutputStream(gcos)) {

            Path sourcePath = Paths.get(sourceDir);

            try (Stream<Path> paths = Files.walk(sourcePath);) {

                for (Path path : (Iterable<Path>) paths::iterator) {
                    TarArchiveEntry tarEntry = new TarArchiveEntry(path.toFile(), sourcePath.relativize(path).toString());
                    try {
                        taos.putArchiveEntry(tarEntry);
                        if (!Files.isDirectory(path)) {
                            Files.copy(path, taos);
                        }
                        taos.closeArchiveEntry();
                    } catch (IOException e) {
                        System.err.printf("压缩为tar.gz 失败: %s%n", e.getMessage());
                    }
                }
                taos.finish();
            }

        }
    }

    /**
     * 将文件压缩成zip
     *
     * @param dir         dir
     * @param zipFileName zip文件名
     * @return {@link Path }
     * @throws IOException IOException
     */
    private static Path compressAllFilesInFolder(String dir, String zipFileName, String sourceDir) throws IOException {
        Path zipFilePath = Paths.get(dir, zipFileName);
        try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFilePath))) {
            Path sourcePath = Paths.get(sourceDir);
            try (Stream<Path> paths = Files.walk(sourcePath);) {
                for (Path path : (Iterable<Path>) paths::iterator) {
                    ZipEntry zipEntry = new ZipEntry(sourcePath.relativize(path).toString());
                    if (!Files.isDirectory(path)) {
                        zipOut.putNextEntry(zipEntry);
                        Files.copy(path, zipOut);
                        Files.delete(path);
                        zipOut.closeEntry();
                    }
                }
                zipOut.finish();
            }
        }

        return zipFilePath;
    }

    /**
     * 将文件压缩成zip
     *
     * @param dir         dir
     * @param zipFileName zip文件名
     * @param fileName    文件名
     * @param filePath    文件路径
     * @return {@link Path }
     * @throws IOException IOException
     */
    private static Path compressFilesIntoZip(String dir, String zipFileName, String fileName, Path filePath) throws IOException {
        Path zipFilePath = Paths.get(dir, zipFileName);
        try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFilePath))) {
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOut.putNextEntry(zipEntry);
            if (!zipEntry.isDirectory()) {
                Files.copy(filePath, zipOut);
            }
            zipOut.closeEntry();
        }
        return zipFilePath;
    }

    /**
     * 上传文件
     *
     * @param outputFilePath 输出文件路径
     * @param bakfilesDir    bakfiles目录
     */
    private static void uploadFiles(String outputFilePath, String bakfilesDir) {
        String presignedUrl = S3Utils.getObsTemporaryVoucher(mockAccount, outputFilePath, null, HttpMethodEnum.PUT.getOperationType());
        try {
            log.info("presignedUrl: " + presignedUrl);
            spikSsl();
            URL url = new URL(presignedUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("PUT");
            connection.setDoOutput(true);
            // 上传文件内容
            compressDirectoryToTarGz(connection.getOutputStream(), bakfilesDir);


            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                System.out.println("File uploaded successfully!");
            } else {
                System.out.println("File upload failed. Response code: " + responseCode);
            }
        } catch (Exception e) {
            log.error("上传文件失败", e);
        }
    }

    private static void spikSsl() {
        // 创建一个信任所有证书的 TrustManager
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
        };
        SSLContext sc = null;

        try {
            // 创建一个忽略证书验证的 SSLContext
            sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new SecureRandom());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
    }

    /**
     * 将目录压缩到targz
     *
     * @param byteArrayOutputStream 字节数组输出流
     * @param sourceDir             源目录
     * @throws IOException IOException
     */
    private static void compressDirectoryToTarGz(OutputStream byteArrayOutputStream, String sourceDir) throws IOException {
        try (BufferedOutputStream bos = new BufferedOutputStream(byteArrayOutputStream);
             GzipCompressorOutputStream gcos = new GzipCompressorOutputStream(bos);
             TarArchiveOutputStream taos = new TarArchiveOutputStream(gcos)) {

            Path sourcePath = Paths.get(sourceDir);

            try (Stream<Path> paths = Files.walk(sourcePath)) {
                for (Path path : (Iterable<Path>) paths::iterator) {
                    TarArchiveEntry tarEntry = new TarArchiveEntry(path.toFile(), sourcePath.relativize(path).toString());
                    try {
                        taos.putArchiveEntry(tarEntry);
                        if (!Files.isDirectory(path)) {
                            Files.copy(path, taos);
                        }
                        taos.closeArchiveEntry();
                    } catch (IOException e) {
                        log.error("压缩为tar.gz 失败: {}", e.getMessage());
                    }
                }
                taos.finish();
            }

        }
    }

    /**
     * 压缩嵌套zip文件
     *
     * @param dir         dir
     * @param zipFileName zip文件名
     * @param fileName    文件名
     * @param filePath    文件路径
     * @return {@link Path }
     * @throws IOException IOException
     */
    private static Path compressNestedZipFiles(String dir, String zipFileName, String fileName, Path filePath) throws IOException {
        Path firstZip = compressFilesIntoZip(dir.substring(0, dir.indexOf("/")), zipFileName, fileName, filePath);
        Files.delete(filePath);
        Path secondZip = compressFilesIntoZip("", zipFileName, zipFileName, firstZip);
        Files.delete(firstZip);
        Path path = compressFilesIntoZip(dir, zipFileName, zipFileName, secondZip);
        Files.delete(secondZip);
        return path;
    }

    private static void deleteDirectory(File file) {
        if (file.isDirectory()) {
            for (File subFile : file.listFiles()) {
                deleteDirectory(subFile);
            }
        }
        file.delete();
    }
}
